package com.mephi.oshs.model.tree;

import java.util.*;

/**
 * Стандартная реализация древовидной структуры данных.
 *
 * @param <T> Данные хранимые в узлах дерева.
 */
public class Tree<T> implements ITree<T> {

    private static final int indent = 2;

    private final List<ITree<T>> leafs = new ArrayList<ITree<T>>();

    private Map<T, ITree<T>> locate = new HashMap<T, ITree<T>>();

    private ITree<T> parent = null;

    private T head;

    public Tree(final T head) {
        this.head = head;
        locate.put(head, this);
    }

    public static <T> Collection<T> getSuccessors(final T of, final Collection<ITree<T>> in) {
        for (ITree<T> tree : in) {
            if (tree.contains(of)) {
                return tree.getSuccessors(of);
            }
        }
        return new ArrayList<T>();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void addLeaf(final T root, final T leaf) {
        if (locate.containsKey(root)) {
            locate.get(root).addLeaf(leaf);
        } else {
            addLeaf(root).addLeaf(leaf);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Tree<T> addLeaf(final T leaf) {
        Tree<T> t = new Tree<T>(leaf);
        leafs.add(t);
        t.parent = this;
        t.locate = this.locate;
        locate.put(leaf, t);
        return t;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Tree<T> setAsParent(final T parentRoot) {
        Tree<T> t = new Tree<T>(parentRoot);
        t.leafs.add(this);
        this.parent = t;
        t.locate = this.locate;
        t.locate.put(head, this);
        t.locate.put(parentRoot, t);
        return t;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public T getHead() {
        return head;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public ITree<T> getTree(final T element) {
        return locate.get(element);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public ITree<T> getParent() {
        return parent;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Collection<T> getSuccessors(final T root) {
        final Collection<T> successors = new ArrayList<T>();
        final ITree<T> tree = getTree(root);
        if (null != tree) {
            for (final ITree<T> leaf : tree.getSubTrees()) {
                successors.add(leaf.getHead());
            }
        }
        return successors;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Collection<ITree<T>> getSubTrees() {
        return leafs;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String printTree(final int increment) {
        final StringBuilder s = new StringBuilder();
        for (int i = 0; i < increment; ++i) {
            s.append(" ");
        }
        s.append(head);
        for (final ITree<T> child : leafs) {
            s.append("\n").append(child.printTree(increment + indent));
        }
        return s.toString();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean contains(final T of) {
        return this.locate.containsKey(of);
    }

    @Override
    public String toString() {
        return printTree(0);
    }
}